package com.ifast.sys.filter;


import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.google.gson.Gson;
import com.ifast.common.csrftoken.CsrfToken;
import com.ifast.common.csrftoken.CsrfTokenCheckException;
import com.ifast.common.csrftoken.util.ObjectUtil;
import com.ifast.common.csrftoken.util.StringUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 项目名称： 防止csrf
 * 类名称：CsrfTokenInterceptor
 * 类描述：  csrftoken认证拦截器
 * 创建人：ShiQiang 
 * 创建时间：2017年7月17日 上午11:41:32
 * 修改人：Administrator 
 * 修改时间：2017年7月17日 上午11:41:32
 * 修改备注： 
 * @version
 */
@Slf4j
public class CsrfTokenInterceptor extends HandlerInterceptorAdapter {
	
	private String tokenKey;
    private int    maxTokens;
    
    public String getTokenKey() {
        return tokenKey;
    }

    public void setTokenKey(String tokenKey) {
        this.tokenKey = StringUtil.trimToNull(tokenKey);
    }
    
    public int getMaxTokens() {
        return maxTokens;
    }

    public void setMaxTokens(int maxTokens) {
        this.maxTokens = maxTokens;
    } 
 
	//解析token 
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        
    	 
    		tokenKey = ObjectUtil.defaultIfNull(tokenKey, CsrfToken.DEFAULT_TOKEN_KEY);
    		String xrq = request.getHeader("X-Requested-With");  
            if(StringUtils.isBlank(xrq)){
            	
            	//验证所有post请求
            	if("POST".equalsIgnoreCase(request.getMethod())){
            		String tokenFromRequest = StringUtil.trimToNull(request.getParameter(tokenKey));
                    
                    if (tokenFromRequest != null) {
                    	HttpSession session = request.getSession();

                        // 先检查longLiveToken，如果匹配，则不用检查uniqueToken了。
                        if (!tokenFromRequest.equals(CsrfToken.getLongLiveTokenInSession(session))) {
                            List<String> tokensInSession = CsrfToken.getTokensInSession(session, tokenKey);

                            if (!tokensInSession.contains(tokenFromRequest)) {
                                    
                                //验证不同过跳转
                                log.info("csrf验证：没有通过！");  
                                //response.sendRedirect(request.getContextPath() + Const.REPUT);
                                throw new CsrfTokenCheckException("重复提交"); 
                               // return false;   
                            } else {
                                // 如果符合，则清除session中相应的token，以防止再次使用它
                                tokensInSession.remove(tokenFromRequest); 
                                CsrfToken.setTokensInSession(session, tokenKey, tokensInSession);
                                return true;    
                            }
                        }
                    }else{
                    	//验证不通过跳转
                        log.info("csrf验证：没有通过！");  
                        throw new CsrfTokenCheckException("重复提交"); 
                       // return false; 
                    }
            	}
                
            }else{
            	 
            	
            	//验证所有post请求
            	if("get".equalsIgnoreCase(request.getMethod())){
            		 String tokenFromRequest = StringUtil.trimToNull(request.getHeader(tokenKey));
                     
                     if(tokenFromRequest != null){
                    	
                     	HttpSession session = request.getSession();
                         // 先检查longLiveToken，如果匹配，则不用检查uniqueToken了。
                         if (!tokenFromRequest.equals(CsrfToken.getLongLiveTokenInSession(session))) {
                             List<String> tokensInSession = CsrfToken.getTokensInSession(session, tokenKey);

                             if (!tokensInSession.contains(tokenFromRequest)) {
                                     
                             	 Map<String,String> map = new HashMap<String,String>();
                             	 map.put("msg", "验证错误!");
                             	 map.put("code", "503");
                                 response.setContentType("application/json;charset=UTF-8");  
                                 PrintWriter out = response.getWriter();  
                                 out.print(new Gson().toJson(map));  
                                 response.flushBuffer();  
                                 return false;    
                             } else {
                                 // 如果符合，则清除session中相应的token，以防止再次使用它
                                 tokensInSession.remove(tokenFromRequest); 
                                 CsrfToken.setTokensInSession(session, tokenKey, tokensInSession);
                                 return true;    
                             }
                         }
                     }else{
                    	 Map<String,String> map = new HashMap<String,String>();
                     	 map.put("msg", "验证错误!");
                     	 map.put("code", "403");
                     	 response.setStatus(403);
                         response.setContentType("application/json;charset=UTF-8");  
                         PrintWriter out = response.getWriter();  
                         out.print(new Gson().toJson(map));  
                         response.flushBuffer();  
                         return false;    
                     }
            	}
           } 
               
           try {
               // 在thread上下文中保存当前的tokenKey，以便使其它csrfToken的检查都能使用统一的key。
               CsrfToken.setContextTokenConfiguration(tokenKey, maxTokens);
               return true; 
           } finally {
               CsrfToken.resetContextTokenConfiguration();
           }  
        
    } 
    
    private CsrfToken getToken(HttpServletRequest request){
    	CsrfToken.setContextTokenConfiguration(tokenKey, maxTokens);
		CsrfToken csrftoken = new CsrfToken(request);
		return csrftoken;
    }
    
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception { 
    	CsrfToken csrftoken = getToken(request);
		String key = CsrfToken.getKey();
		String xrq = request.getHeader("X-Requested-With");  
        if(StringUtils.isNotBlank(xrq)){ 
        	//ajax请求
            response.addHeader(key, csrftoken.getUniqueToken());
           // response.addHeader(key+"Long",csrftoken.getLongLiveToken());
        } 
    	if(modelAndView != null){ 
    		//根据具体需要添加在页面获取两个csrf的值
    		//表单重复提交
        	modelAndView.addObject(key,csrftoken.getUniqueToken());
        	//csrf保存session时长
        	//modelAndView.addObject(key+"Long",csrftoken.getLongLiveToken()); 
    	} 
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }
}
